import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:auto_route/auto_route.dart';
import 'package:dio/dio.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sonora/bloc/base/sj_base_bloc.dart';
import 'package:sonora/bloc/home/sj_home_event.dart';
import 'package:sonora/bloc/home/sj_home_state.dart';
import 'package:sonora/bloc/mine/sj_mine_event.dart';
import 'package:sonora/bloc/mine/sj_mine_state.dart';
import 'package:sonora/events/sj_video_event.dart';
import 'package:sonora/events/sj_works_event.dart';
import 'package:sonora/models/sj_block_list_item_data.dart';
import 'package:sonora/models/sj_friend_list_item_data.dart';
import 'package:sonora/models/sj_recharge_create_data.dart';
import 'package:sonora/models/sj_response_data.dart';
import 'package:sonora/models/sj_works_model.dart';
import 'package:sonora/network/sj_network.dart';
import 'package:sonora/repository/sj_home_repository.dart';
import 'package:sonora/repository/sj_mine_repository.dart';
import 'package:sonora/utils/sj_media_util.dart';
import 'package:sonora/utils/sj_storage_utils.dart';
import 'package:sonora/utils/sj_utils.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sonora/views/navigation/routes/sj_app_router.dart';
import 'package:in_app_purchase/in_app_purchase.dart';

import '../../config/sj_app_initial.dart';
import '../../models/sj_login_response_data.dart';

class SjMineBloc extends SjBaseBloc<SjMineEvent, SjMineState> {
  final SjMineRepository repository;
  late final InAppPurchase _appPurchase = InAppPurchase.instance;

  SjMineBloc(this.repository) : super(const SjMineState()) {
    on<SjMineEventLoadedUserInfo>(_onLoadUserInfo);
    on<SjMineEventLoadedGoodSearchList>(_onLoadGoodSearchList);
    on<SjMineEventAddHeaderImage>(_onAddHeaderImage);
    on<SjMineEventTakePhoto>(_onTakePhoto);
    on<SjMineEventLogout>(_onLogout);
    on<SjMineEventDeleteAccount>(_onDeleteAccount);
    on<SjMineEventNickNameChanged>(_onNickNameChange);
    on<SjMineEventBirthdayChanged>(_onBirthdayChange);
    on<SjMineEventCountryChanged>(_onCountryChange);
    on<SjMineEventSaveUserInfo>(_onSaveUserInfo);
    on<SjMineEventInitialPurchase>(_onInitialPurchase);
    on<SjMineEventAutoTranslate>(_onAutoTranslate);
    on<SjMineEventPagePop>(_onPagePop);
    on<SjMineEventUnFriend>(_onUnFriend);
    on<SjMineEventFriend>(_onAddFriend);
    on<SjMineEventRemoveBlock>(_onRemoveBlock);
    on<SjMineEventInsertRecord>(_onAddBlock);
    on<SjMineEventLoadedFollowList>(_onLoadFollowList);
    on<SjMineEventLoadedBlockList>(_onLoadBlockList);
    on<SjMineEventLoadWorksList>(_onLoadWorksList);
    on<SjMineEventDeleteWorks>(_onDeleteWorks);
  }

  FutureOr<void> _onPagePop(
      SjMineEventPagePop event, Emitter<SjMineState> emit) async {
    emit(state.copyWith(
      loading: false,
      headerImage: null,
      errorMsg: null,
      successMsg: null,
    ));
  }

  FutureOr<void> _onLoadUserInfo(
      SjMineEventLoadedUserInfo event, Emitter<SjMineState> emit) async {
    emit(state.copyWith(loading: true));
    try {
      SjLoginData? loginData = await SjStorageUtils.getLoginInfo();
      String userId = '';
      if (loginData != null && loginData.userInfo != null) {
        userId = loginData.userInfo?.userId ?? '';
      }
      if (state.userInfoData == null) {
        SjUserDetailData? userDetail = await SjStorageUtils.getUserDetail();
        if (userDetail != null) {
          userDetail.autoTranslate = await SjStorageUtils.getAutoTranslate();
          emit(state.copyWith(userInfoData: userDetail, loading: false));
        }
      }
      SjResponseData responseData = await repository.getUserInfo(userId);
      if (responseData.data != null &&
          responseData.success != null &&
          responseData.success == true) {
        Map<String, dynamic> jsonMap = responseData.data;
        SjUserDetailData userDetailData = SjUserDetailData.fromJson(jsonMap);
        userDetailData.autoTranslate = await SjStorageUtils.getAutoTranslate();
        emit(state.copyWith(
          userInfoData: userDetailData,
          loading: false,
          headerImage: null,
          errorMsg: null,
          successMsg: null,
        ));
        SjStorageUtils.saveUserDetail(jsonMap);
      }
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onLoadFollowList(
      SjMineEventLoadedFollowList event, Emitter<SjMineState> emit) async {
    try {
      final list = await repository.friendList();
      emit(state.copyWith(friendList: list));
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onAutoTranslate(
      SjMineEventAutoTranslate event, Emitter<SjMineState> emit) async {
    try {
      final autoTranslate = event.autoTranslate;
      SjUserDetailData infoData =
          SjUserDetailData.fromJson(state.userInfoData!.toJson());
      infoData.autoTranslate = !autoTranslate;
      SjStorageUtils.saveAutoTranslate(!autoTranslate);
      emit(state.copyWith(userInfoData: infoData));
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onLoadGoodSearchList(
      SjMineEventLoadedGoodSearchList event, Emitter<SjMineState> emit) async {
    try {
      final list = await repository.goodsSearch();
      emit(state.copyWith(goodSearch: list));
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onAddHeaderImage(
      SjMineEventAddHeaderImage event, Emitter<SjMineState> emit) async {
    var photosStatus = await Permission.photos.status;
    if (photosStatus.isDenied) {
      PermissionStatus result = await Permission.photos.request();
      if (result.isDenied || result.isPermanentlyDenied) {
      EasyLoading.showToast('You have not authorised this permission.',
          duration: const Duration(seconds: 3));
        return;
      }
    } else if (photosStatus.isPermanentlyDenied) {
      EasyLoading.showToast('You have not authorised this permission.',
          duration: const Duration(seconds: 3));
      return;
    }
    String? imgPath = await SjMediaUtil.instance.pickImage();
    if (imgPath != null) {
      emit(state.copyWith(
          headerImage: imgPath,
          isEdit: _dealIsEdit(headerImage: imgPath),
          loading: false,
          errorMsg: null,
          successMsg: null));
    }
  }

  FutureOr<void> _onTakePhoto(
      SjMineEventTakePhoto event, Emitter<SjMineState> emit) async {
    var cameraStatus = await Permission.camera.status;
    if (cameraStatus.isDenied) {
      PermissionStatus result = await Permission.camera.request();
      if (result.isDenied || result.isPermanentlyDenied) {
      EasyLoading.showToast('You have not authorised this permission.',
          duration: const Duration(seconds: 3));
        return;
      }
    } else if (cameraStatus.isPermanentlyDenied) {
      EasyLoading.showToast('You have not authorised this permission.',
          duration: const Duration(seconds: 3));
      return;
    }
    final picker = ImagePicker();
    final pickedFile = await picker.pickImage(source: ImageSource.camera);
    String? imgPath = pickedFile?.path;
    if (imgPath != null) {
      emit(state.copyWith(
          headerImage: imgPath,
          isEdit: _dealIsEdit(headerImage: imgPath),
          loading: false,
          errorMsg: null,
          successMsg: null));
    }
  }

  Future<bool> copyFile(String srcPath, String destPath) async {
    try {
      File source = File(srcPath);
      File destination = File(destPath);
      await destination.create(recursive: true);
      File file = await source.copy(destination.path);
      if (file.existsSync()) {
        return true;
      }
    } catch (e, s) {}
    return false;
  }

  Future<String?> _onUpdateHeaderImage() async {
    if (state.headerImage == null) return null;
    try {
      SjResponseData responseData = await repository.getOssPolicy();
      File file = File(state.headerImage ?? '');
      DateTime dateTime = DateTime.now();
      String fileName = file.path.split('/').last;
      String format = fileName.split('.').last;
      int imageTimeName = dateTime.millisecondsSinceEpoch +
          (dateTime.microsecondsSinceEpoch ~/ 1000000);
      String imageName = '$imageTimeName.$format';
      String host = responseData.data!['host'];
      String dir = responseData.data!['dir'];
      var filePath =
          await MultipartFile.fromFile(file.path, filename: fileName);
      final formData = FormData.fromMap({
        'ossaccessKeyId': responseData.data['accessKeyId'],
        'policy': responseData.data['policy'],
        'signature': responseData.data['signature'],
        'callback': responseData.data['callback'],
        'key': '$dir/$imageName',
        'file': filePath
      });

      try {
        Dio dio = SjNetwork().clientDio;
        var response = await dio.post(host, data: formData);
        if (response.data != null && response.data['data'] != null) {
          final result =
              await repository.updateAvatar(response.data['data']['filename']);
          if (result.data is Map<String, dynamic>) {
            return result.data['middleThumbUrl'];
          }
        }
        return null;
      } catch (e) {
        return null;
      }
    } catch (e) {
      return null;
    }
  }

  FutureOr<void> _onLogout(
      SjMineEventLogout event, Emitter<SjMineState> emit) async {
    try {
      SjResponseData response = await repository.globalLogout();
      if (response.success != null && response.success == true) {
        await SjStorageUtils.clearLoginInfo();
        // event.context.router.pushAndPopUntil(
        //   SjLoginRoute(),
        //   predicate: (route) => false, // 清空栈
        // );
        eventBus.fire(SjVideoBusEvent(destroy: true));
        event.context.router.replaceAll([const SjLoginRoute()]);
      } else {}
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onDeleteAccount(
      SjMineEventDeleteAccount event, Emitter<SjMineState> emit) async {
    try {
      SjResponseData response = await repository.deleteAccount();
      if (response.success != null && response.success == true) {
        await SjStorageUtils.clearLoginInfo();
        // event.context.router.pushAndPopUntil(
        //   SjLoginRoute(),
        //   predicate: (route) => false, // 清空栈
        // );
        eventBus.fire(SjVideoBusEvent(destroy: true));
        event.context.router.replaceAll([const SjLoginRoute()]);
      } else {}
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onAddFriend(
      SjMineEventFriend event, Emitter<SjMineState> emit) async {
    SjResponseData response = await repository.addFriend(event.followUserId);
    if (response.success != null && response.success == true) {
      add(const SjMineEventLoadedFollowList());
      EasyLoading.showSuccess('Followed successfully',
          duration: const Duration(seconds: 3));
    }
  }

  FutureOr<void> _onUnFriend(
      SjMineEventUnFriend event, Emitter<SjMineState> emit) async {
    try {
      SjResponseData response = await repository.unFriend(event.followUserId);
      if (response.success != null && response.success == true) {
        List<SjFriendListItemData> list =
            List<SjFriendListItemData>.from(state.friendList ?? []);
        list.removeWhere((element) => element.userId == event.followUserId);
        emit(state.copyWith(friendList: list));
      }
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onAddBlock(
      SjMineEventInsertRecord event, Emitter<SjMineState> emit) async {
    SjResponseData response = await repository.insertRecord(
        event.broadcasterId, event.complainCategory, event.complainSub);
    if (response.success != null && response.success == true) {
      add(SjMineEventFriend(followUserId: event.broadcasterId ?? ''));
      add(const SjMineEventLoadedBlockList());
      EasyLoading.showSuccess('Block successfully',
          duration: const Duration(seconds: 3));
    }
  }

  FutureOr<void> _onRemoveBlock(
      SjMineEventRemoveBlock event, Emitter<SjMineState> emit) async {
    try {
      SjResponseData response =
          await repository.removeBlock(event.broadcasterId);
      if (response.success != null && response.success == true) {
        List<SjBlockListItemData> list =
            List<SjBlockListItemData>.from(state.blokList ?? []);
        list.removeWhere(
            (element) => element.broadcasterId == event.broadcasterId);
        emit(state.copyWith(blokList: list));
        eventBus.fire(SjVideoBusEvent(isReload: true));
      }
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onLoadBlockList(
      SjMineEventLoadedBlockList event, Emitter<SjMineState> emit) async {
    try {
      final list = await repository.blockList();
      emit(state.copyWith(blokList: list));
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onNickNameChange(
      SjMineEventNickNameChanged event, Emitter<SjMineState> emit) async {
    SjUserDetailData userInfo =
        SjUserDetailData.fromJson(state.userInfoData!.toJson());
    userInfo.nickname = event.nickName;
    emit(state.copyWith(
        nickName: event.nickName,
        userInfoData: userInfo,
        isEdit: _dealIsEdit(nickName: event.nickName),
        loading: false,
        errorMsg: null,
        successMsg: null));
  }

  FutureOr<void> _onCountryChange(
      SjMineEventCountryChanged event, Emitter<SjMineState> emit) async {
    SjUserDetailData userInfo =
        SjUserDetailData.fromJson(state.userInfoData!.toJson());
    userInfo.country = event.country;
    emit(state.copyWith(
        birthday: event.country,
        userInfoData: userInfo,
        isEdit: _dealIsEdit(country: event.country),
        loading: false,
        errorMsg: null,
        successMsg: null));
  }

  FutureOr<void> _onBirthdayChange(
      SjMineEventBirthdayChanged event, Emitter<SjMineState> emit) async {
    SjUserDetailData userInfo =
        SjUserDetailData.fromJson(state.userInfoData!.toJson());
    userInfo.birthday = event.birthday;
    emit(state.copyWith(
        birthday: event.birthday,
        userInfoData: userInfo,
        isEdit: _dealIsEdit(birthday: event.birthday),
        loading: false,
        errorMsg: null,
        successMsg: null));
  }

  FutureOr<void> _onSaveUserInfo(
      SjMineEventSaveUserInfo event, Emitter<SjMineState> emit) async {
    SjResponseData responseData;
    String? updateHeaderUrl;
    SjUserDetailData userInfo =
        SjUserDetailData.fromJson(state.userInfoData!.toJson());
    EasyLoading.show(status: 'Loading...');
    emit(state.copyWith(loading: true, errorMsg: null, successMsg: null));
    if (state.headerImage != null) {
      updateHeaderUrl = await _onUpdateHeaderImage();
      if (updateHeaderUrl == null || updateHeaderUrl.isEmpty) {
        EasyLoading.dismiss();
        EasyLoading.showError('Edit failed',
            duration: const Duration(seconds: 3));
        // update header fail
        emit(state.copyWith(
            loading: false, errorMsg: 'Edit failed', successMsg: null));
        return;
      } else if (_dealFormFieldIsEdit()) {
        // update userInfo
        responseData = await repository.saveUserInfo(
            state.userInfoData?.nickname,
            state.userInfoData?.country,
            state.userInfoData?.birthday);
      } else {
        // only update header success
        EasyLoading.dismiss();
        EasyLoading.showSuccess('Edit successfully',
            duration: const Duration(seconds: 3));
        userInfo.avatarUrl = updateHeaderUrl;
        emit(state.copyWith(
            userInfoData: userInfo,
            loading: false,
            errorMsg: null,
            headerImage: null,
            successMsg: 'Edit successfully',
            isEdit: false));
        return;
      }
    } else {
      responseData = await repository.saveUserInfo(state.userInfoData?.nickname,
          state.userInfoData?.country, state.userInfoData?.birthday);
    }

    if (updateHeaderUrl != null && updateHeaderUrl.isNotEmpty) {
      userInfo.avatarUrl = updateHeaderUrl;
    }
    if (responseData.success != null && responseData.success == true) {
      EasyLoading.dismiss();
      EasyLoading.showSuccess('Edit successfully',
          duration: const Duration(seconds: 3));
      emit(state.copyWith(
          userInfoData: userInfo,
          loading: false,
          errorMsg: null,
          headerImage: null,
          nickName: null,
          birthday: null,
          country: null,
          successMsg: 'Edit successfully',
          isEdit: false));
    } else {
      EasyLoading.dismiss();
      EasyLoading.showSuccess('Edit failed',
          duration: const Duration(seconds: 3));
      emit(state.copyWith(
          loading: false, errorMsg: 'Edit failed', successMsg: null));
    }

    // emit(state.copyWith(birthday: event.birthday, userInfoData: userInfo, isEdit: _dealIsEdit()));
  }

  bool _dealIsEdit(
      {String? headerImage,
      String? nickName,
      String? birthday,
      String? country}) {
    if (nickName != null && nickName.isEmpty) {
      return false;
    }
    if (headerImage != null ||
        birthday != null ||
        country != null ||
        (nickName != null && nickName.isNotEmpty)) {
      return true;
    }
    if (state.nickName != null && state.nickName!.isEmpty) {
      return false;
    }
    if (state.headerImage != null ||
        state.birthday != null ||
        state.country != null) {
      return true;
    }
    return false;
  }

  bool _dealFormFieldIsEdit() {
    if (state.birthday != null && state.birthday!.isEmpty) {
      return false;
    }
    if (state.birthday != null || state.country != null) {
      return true;
    }
    return false;
  }

  FutureOr<void> _onInitialPurchase(
      SjMineEventInitialPurchase event, Emitter<SjMineState> emit) async {
    emit(state.copyWith(loading: true));
    EasyLoading.show(status: 'Loading...');
    final availabel = await _appPurchase.isAvailable();
    if (availabel) {
      try {
        SjResponseData responseData =
            await repository.rechargeCreate(event.goodCode);
        if (responseData.success != null && responseData.success == true) {
          SjRechargeCreateData rechargeCreateData =
              SjRechargeCreateData.fromJson(responseData.data);
          final ProductDetailsResponse productDetailsResponse =
              await _appPurchase.queryProductDetails(<String>{event.goodCode});
          if (productDetailsResponse.notFoundIDs.isNotEmpty) {
            emit(state.copyWith(
                loading: false, errorMsg: 'Products not found.'));
            EasyLoading.dismiss();
            EasyLoading.showError('Products not found.',
                duration: const Duration(seconds: 3));
          } else {
            final ProductDetails product =
                productDetailsResponse.productDetails.first;
            final PurchaseParam purchaseParam =
                PurchaseParam(productDetails: product);

            _appPurchase.buyConsumable(purchaseParam: purchaseParam);

            _appPurchase.purchaseStream.listen((purchaseDetailsList) {
              for (PurchaseDetails purchaseDetails in purchaseDetailsList) {
                if (purchaseDetails.status == PurchaseStatus.purchased) {
                  rechargePayment(rechargeCreateData, purchaseDetails);
                } else if (purchaseDetails.status == PurchaseStatus.restored) {
                  EasyLoading.dismiss();
                  EasyLoading.showError(purchaseDetails.error!.message,
                      duration: const Duration(seconds: 3));
                  _appPurchase.completePurchase(purchaseDetails);
                } else if (purchaseDetails.status == PurchaseStatus.error) {
                  EasyLoading.dismiss();
                  EasyLoading.showError(purchaseDetails.error!.message,
                      duration: const Duration(seconds: 3));
                  _appPurchase.completePurchase(purchaseDetails);
                } else if (purchaseDetails.status == PurchaseStatus.canceled) {
                  EasyLoading.dismiss();
                  EasyLoading.showError('Cancel Purchase',
                      duration: const Duration(seconds: 3));
                  _appPurchase.completePurchase(purchaseDetails);
                }
              }
            });
          }
        }
      } catch (e) {
        emit(state.copyWith(loading: false, errorMsg: e.toString()));
        EasyLoading.dismiss();
        EasyLoading.showError(e.toString(),
            duration: const Duration(seconds: 3));
      }
    } else {
      EasyLoading.dismiss();
      EasyLoading.showError('In-App Purchase not available.',
          duration: const Duration(seconds: 3));
      emit(state.copyWith(
          loading: false, errorMsg: 'In-App Purchase not available.'));
    }
  }

  Future<void> rechargePayment(SjRechargeCreateData rechargeCreateData,
      PurchaseDetails purchaseDetails) async {
    try {
      SjResponseData responseData = await repository.rechargePayment(
          rechargeCreateData.orderNo!,
          purchaseDetails.verificationData.serverVerificationData,
          purchaseDetails.purchaseID!);
      if (responseData.success != null && responseData.success == true) {
        add(SjMineEvent.loadedUserInfo());
      }
      await _appPurchase.completePurchase(purchaseDetails);
      EasyLoading.dismiss();
    } catch (e) {
      EasyLoading.dismiss();
      EasyLoading.showError(e.toString(), duration: const Duration(seconds: 3));
    }
  }

  Future<void> _loadUserInfo() async {}

  FutureOr<void> _onLoadWorksList(
      SjMineEventLoadWorksList event, Emitter<SjMineState> emit) async {
    try {
      SjLoginData? loginData = await SjStorageUtils.getLoginInfo();
      String userId = '';
      if (loginData != null && loginData.userInfo != null) {
        userId = loginData.userInfo?.userId ?? '';
      }
      final list = await SjStorageUtils.getWorksByUserId(userId);
      if (list != null && list.length == 1) {
        list.add(const SjWorksModel(isPlaceholder: true));
      }
      emit(state.copyWith(worksList: list));
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }

  FutureOr<void> _onDeleteWorks(
      SjMineEventDeleteWorks event, Emitter<SjMineState> emit) async {
    try {
      SjLoginData? loginData = await SjStorageUtils.getLoginInfo();
      String userId = '';
      if (loginData != null && loginData.userInfo != null) {
        userId = loginData.userInfo?.userId ?? '';
      }
      await SjStorageUtils.deleteWorks(event.worksId);
      final list = await SjStorageUtils.getWorksByUserId(userId);
      emit(state.copyWith(worksList: list));
      eventBus.fire(SjWorksBusEvent(isDelete: true));
    } catch (e) {
      emit(state.copyWith(loading: false, errorMsg: e.toString()));
    }
  }
}
